//
//  STDBlendShaderStream.cpp
//  STDRenderSDK
//
//  Created by 陈学明 on 2023/3/20.
//

#include <stdio.h>
#include "STDSShaderStream.hpp"
#include "Basic/graphic/STDSOpenGL.h"

/// 标准混合
const char *gl_fra_Blend_Normal = SHADER_SRC(
     SHDR_FRAG_HEAD(highp)
     uniform sampler2D uTexture;
     uniform sampler2D uTexture2;
     uniform float uParam;

     void main() {
         vec4 overlay = SHDR_TEXTURE2D(uTexture2, vTexCoord);
         vec4 base = SHDR_TEXTURE2D(uTexture, vTexCoord);
         vec3 dst;
         float alpha;
         dst = overlay.rgb + base.rgb * (1.0 - overlay.a);
         alpha = overlay.a + uParam * (base.a - (overlay.a * base.a));
         vec4 outColor = vec4(dst, alpha);
         SHDR_OUTCOLOR
     }
                                             
);

/// 正片叠底
const char *gl_fra_Blend_Multiply = SHADER_SRC(
        precision highp float;

        varying vec2 vTexCoord;
        uniform sampler2D uTexture;
        uniform sampler2D uTexture2;
        uniform float uParam;

        void main() {
            vec4 overlay = texture2D(uTexture2, vTexCoord);
            vec4 base = texture2D(uTexture, vTexCoord);
            vec4 clr = overlay * base + base * (1. - overlay.a) + overlay * (1. - base.a);
            gl_FragColor = clr;
        }
);

/// 屏幕
const char *gl_fra_Blend_Screen = SHADER_SRC(
        SHDR_FRAG_HEAD(highp)
        uniform sampler2D uTexture;
        uniform sampler2D uTexture2;
        uniform float uParam;

        void main() {
            vec4 overlay = SHDR_TEXTURE2D(uTexture2, vTexCoord);
            vec4 base = SHDR_TEXTURE2D(uTexture, vTexCoord);
            vec4 white = vec4(1.0);
            vec4 outColor = white - ((white - base) * (white - overlay));
            SHDR_OUTCOLOR
        }
                                        
);

/// 叠加
const char *gl_fra_Blend_Overlay = SHADER_SRC(
        precision highp float;

        varying vec2 vTexCoord;
        uniform sampler2D uTexture;
        uniform sampler2D uTexture2;
        uniform float uParam;

        float getOverlay(float clr_overlay, float clr_base, float alpha_overlay, float alpha_base) {
            float clr;
            if(2.0 * clr_base < alpha_base) {
                clr = 2.0 * clr_overlay * clr_base + clr_overlay * (1.0 - alpha_base) + clr_base * (1.0 - alpha_overlay);
            } else {
                clr = alpha_overlay * alpha_base - 2.0 * (alpha_base - clr_base) * (alpha_overlay - clr_overlay) + clr_overlay * (1.0 - alpha_base) + clr_base * (1.0 - alpha_overlay);
            }
            return min(1.0, clr);
        }

        void main() {
            vec4 overlay = texture2D(uTexture2, vTexCoord);
            vec4 base = texture2D(uTexture, vTexCoord);

            vec4 dst;
            dst.r = getOverlay(overlay.r, base.r, overlay.a, base.a);
            dst.g = getOverlay(overlay.g, base.g, overlay.a, base.a);
            dst.b = getOverlay(overlay.b, base.b, overlay.a, base.a);
            dst.a = overlay.a + uParam * (base.a - (overlay.a * base.a));

            gl_FragColor = dst;
        }
);

/// 变暗
const char *gl_fra_Blend_Darken = SHADER_SRC(
        precision highp float;

        varying vec2 vTexCoord;
        uniform sampler2D uTexture;
        uniform sampler2D uTexture2;
        uniform float uParam;

        void main() {
            vec4 overlay = texture2D(uTexture2, vTexCoord);
            vec4 base = texture2D(uTexture, vTexCoord);
            vec4 dst = vec4(min(overlay.rgb * base.a, base.rgb * overlay.a) + overlay.rgb * (1.0 - base.a) + base.rgb * (1.0 - overlay.a), 1.0);
            dst.a = overlay.a + uParam * (base.a - (overlay.a * base.a));
            gl_FragColor = dst;
        }
);

/** 变亮 **/
const char *gl_fra_Blend_Lighten = SHADER_SRC(
        precision highp float;

        varying vec2 vTexCoord;
        uniform sampler2D uTexture;
        uniform sampler2D uTexture2;
        uniform float uParam;

        void main() {
            vec4 overlay = texture2D(uTexture2, vTexCoord);
            vec4 base = texture2D(uTexture, vTexCoord);
            vec4 dst;
            dst.rgb = max(overlay.rgb, base.rgb);
            dst.a = overlay.a + uParam * (base.a - (overlay.a * base.a));
            gl_FragColor = dst;
        }
);

/** 颜色减淡 **/
const char *gl_fra_Blend_Color_Dodge = SHADER_SRC(
        precision highp float;

        varying vec2 vTexCoord;
        uniform sampler2D uTexture;
        uniform sampler2D uTexture2;
        uniform float uParam;

        void main() {
            vec4 overlay = texture2D(uTexture2, vTexCoord);
            vec4 base = texture2D(uTexture, vTexCoord);

            vec3 baseOverlayAlphaProduct = vec3(overlay.a * base.a);
            vec3 rightHandProduct = overlay.rgb * (1.0 - base.a) + base.rgb * (1.0 - overlay.a);

            vec3 firstBlendColor = baseOverlayAlphaProduct + rightHandProduct;
            vec3 overlayRGB = clamp((overlay.rgb / clamp(overlay.a, 0.01, 1.0)) * step(0.0, overlay.a), 0.0, 0.99);

            vec3 secondBlendColor = (base.rgb * overlay.a) / (1.0 - overlayRGB) + rightHandProduct;

            vec3 colorChoice = step((overlay.rgb * base.a + base.rgb * overlay.a), baseOverlayAlphaProduct);

            float alpha = overlay.a + uParam * (base.a - (overlay.a * base.a));

            gl_FragColor = vec4(mix(firstBlendColor, secondBlendColor, colorChoice), alpha);
        }
);

/** 颜色加深 **/
const char *gl_fra_Blend_Color_Burn = SHADER_SRC(
        precision highp float;

        varying vec2 vTexCoord;
        uniform sampler2D uTexture;
        uniform sampler2D uTexture2;
        uniform float uParam;

        void main() {
            vec4 overlay = texture2D(uTexture2, vTexCoord);
            vec4 base = texture2D(uTexture, vTexCoord);

            vec4 whiteColor = vec4(1.0);
            float alpha = overlay.a + (base.a - (overlay.a * base.a));
            vec4 clr = whiteColor - (whiteColor - base) / overlay;
            gl_FragColor = vec4(clr.rgb, alpha);
        }
);

/** 强光 **/
const char *gl_fra_Blend_Hard_Light = SHADER_SRC(
        precision highp float;

        varying vec2 vTexCoord;
        uniform sampler2D uTexture;
        uniform sampler2D uTexture2;
        uniform float uParam;

        void main() {
            vec4 overlay = texture2D(uTexture2, vTexCoord);
            vec4 base = texture2D(uTexture, vTexCoord);

            float ra;
            if (2.0 * overlay.r < overlay.a) {
                ra = 2.0 * overlay.r * base.r + overlay.r * (1.0 - base.a) + base.r * (1.0 - overlay.a);
            } else {
                ra = overlay.a * base.a - 2.0 * (base.a - base.r) * (overlay.a - overlay.r) + overlay.r * (1.0 - base.a) + base.r * (1.0 - overlay.a);
            }

            float ga;
            if (2.0 * overlay.g < overlay.a) {
                ga = 2.0 * overlay.g * base.g + overlay.g * (1.0 - base.a) + base.g * (1.0 - overlay.a);
            } else {
                ga = overlay.a * base.a - 2.0 * (base.a - base.g) * (overlay.a - overlay.g) + overlay.g * (1.0 - base.a) + base.g * (1.0 - overlay.a);
            }

            float ba;
            if (2.0 * overlay.b < overlay.a) {
                ba = 2.0 * overlay.b * base.b + overlay.b * (1.0 - base.a) + base.b * (1.0 - overlay.a);
            } else {
                ba = overlay.a * base.a - 2.0 * (base.a - base.b) * (overlay.a - overlay.b) + overlay.b * (1.0 - base.a) + base.b * (1.0 - overlay.a);
            }
            float alpha = overlay.a + base.a - overlay.a * base.a;

            gl_FragColor = vec4(ra, ga, ba, alpha);
        }
);

/** 柔光 **/
const char *gl_fra_Blend_Soft_Light = SHADER_SRC(
        precision highp float;

        varying vec2 vTexCoord;
        uniform sampler2D uTexture;
        uniform sampler2D uTexture2;
        uniform float uParam;

        void main() {
            vec4 overlay = texture2D(uTexture2, vTexCoord);
            overlay = overlay*uParam;
            vec4 base = texture2D(uTexture, vTexCoord);
            lowp float alphaDivisor = base.a + step(base.a, 0.0); // Protect against a divide-by-zero blacking out things in the output

            gl_FragColor = base * (overlay.a * (base / alphaDivisor) + (2.0 * overlay * (1.0 - (base / alphaDivisor)))) + overlay * (1.0 - base.a) + base * (1.0 - overlay.a);

        }
);

/** 差值 **/
const char *gl_fra_Blend_Difference = SHADER_SRC(
        precision highp float;

        varying vec2 vTexCoord;
        uniform sampler2D uTexture;
        uniform sampler2D uTexture2;
        uniform float uParam;

        vec4 getAntiPreMultiColor(vec4 gx_src) {
            float gx_alpha = gx_src.a;
            float gx_factor = (gx_alpha == 0.0) ? 1.0 : 1.0 / gx_alpha;
            vec4 gx_dst = vec4(gx_factor * gx_src.rgb, gx_alpha);
            return gx_dst;
        }

        void main() {
            vec4 overlay = texture2D(uTexture2, vTexCoord);
            vec4 base = texture2D(uTexture, vTexCoord);

            vec3 dst = abs(overlay.rgb - base.rgb);
            float alpha = overlay.a + base.a - overlay.a * base.a;

            gl_FragColor = vec4(dst, alpha);
        }
);

/** 排除 **/
const char *gl_fra_Blend_Exclusion = SHADER_SRC(
        precision highp float;

        varying vec2 vTexCoord;
        uniform sampler2D uTexture;
        uniform sampler2D uTexture2;
        uniform float uParam;

        void main() {
            vec4 overlay = texture2D(uTexture2, vTexCoord);
            vec4 base = texture2D(uTexture, vTexCoord);
            gl_FragColor = vec4((overlay.rgb * base.a + base.rgb * overlay.a - 2.0 * overlay.rgb * base.rgb)
                    + overlay.rgb * (1.0 - base.a) + base.rgb * (1.0 - overlay.a), base.a);
        }
);

/** 相加 **/
const char *gl_fra_Blend_Add = SHADER_SRC(
        precision highp float;

        varying vec2 vTexCoord;
        uniform sampler2D uTexture;
        uniform sampler2D uTexture2;
        uniform float uParam;

        void main() {
            vec4 overlay = texture2D(uTexture2, vTexCoord);
            vec4 base = texture2D(uTexture, vTexCoord);

            vec3 sumRGB = min(overlay.rgb + base.rgb, 1.0);
            float alpha = overlay.a + base.a - overlay.a * base.a;

            gl_FragColor = vec4(sumRGB, alpha);
        }
);

void regShaderForBlend(map<string, shared_ptr<STDSShaderStream>> &shaderMap) {
    shaderMap.insert(make_pair(string(kShaderSRCBlendMultiply), make_shared<STDSShaderStream>(gl_shader_vex_2D, gl_fra_Blend_Multiply)));
    shaderMap.insert(make_pair(string(kShaderSRCBlendSrceen), make_shared<STDSShaderStream>(gl_shader_vex_2D, gl_fra_Blend_Screen)));
    shaderMap.insert(make_pair(string(kShaderSRCBlendOverlay), make_shared<STDSShaderStream>(gl_shader_vex_2D, gl_fra_Blend_Overlay)));
    shaderMap.insert(make_pair(string(kShaderSRCBlendDarken), make_shared<STDSShaderStream>(gl_shader_vex_2D, gl_fra_Blend_Darken)));
    shaderMap.insert(make_pair(string(kShaderSRCBlendLighten), make_shared<STDSShaderStream>(gl_shader_vex_2D, gl_fra_Blend_Lighten)));
    shaderMap.insert(make_pair(string(kShaderSRCBlendColorDodge), make_shared<STDSShaderStream>(gl_shader_vex_2D, gl_fra_Blend_Color_Dodge)));
    shaderMap.insert(make_pair(string(kShaderSRCBlendColorBurn), make_shared<STDSShaderStream>(gl_shader_vex_2D, gl_fra_Blend_Color_Burn)));
    shaderMap.insert(make_pair(string(kShaderSRCBlendHardLight), make_shared<STDSShaderStream>(gl_shader_vex_2D, gl_fra_Blend_Hard_Light)));
    shaderMap.insert(make_pair(string(kShaderSRCBlendSoftLight), make_shared<STDSShaderStream>(gl_shader_vex_2D, gl_fra_Blend_Soft_Light)));
    shaderMap.insert(make_pair(string(kShaderSRCBlendDifference), make_shared<STDSShaderStream>(gl_shader_vex_2D, gl_fra_Blend_Difference)));
    shaderMap.insert(make_pair(string(kShaderSRCBlendExclusion), make_shared<STDSShaderStream>(gl_shader_vex_2D, gl_fra_Blend_Exclusion)));
    shaderMap.insert(make_pair(string(kShaderSRCBlendAdd), make_shared<STDSShaderStream>(gl_shader_vex_2D, gl_fra_Blend_Add)));
    shaderMap.insert(make_pair(string(kShaderSRCBlendNormal), make_shared<STDSShaderStream>(gl_shader_vex_2D, gl_fra_Blend_Normal)));
}
